package dk.aau.walkietalkie.network;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InterfaceAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;

import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.util.Log;
import dk.aau.walkietalkieui.WalkieTalkieApplication;

public class UDPBroadcastSender implements Runnable {
	
	// How often a broadcast is sent to all the available devices
	// public value for using in the Receiver and AliveCounter! 
	public static final int BROADCAST_INTERVAL = 3000; // ms
	// Largest possible name = MAX_PACKET_SIZE - length(BROADCAST_TOKEN)
	// public value for using in the Receiver
	public static final int MAX_PACKET_SIZE = 256;     // bytes
	public static final String GENERIC_BROADCAST_ADDRESS = "255.255.255.255";
	public static final String BROADCAST_TOKEN = "WALKIE_TALKIE_USERID=";
	private static final String DEBUG_TAG = "BroadcastSender";

	private boolean mStopSignal;
	private DatagramSocket mDsocket;
	private String mOwnIP;

	public void stopBroadcast() {
		mStopSignal = true;
	}

	@Override
	public void run() {

		mStopSignal = false;
		
		// Open a random port to send the package
		try {
			mDsocket = new DatagramSocket();
			mDsocket.setBroadcast(true);
			mOwnIP = NetworkHelper.getDottedDecimalIP(NetworkHelper.getLocalIPAddress());
		} catch (SocketException e) {
			Log.e(DEBUG_TAG, "Could not create socket: " + e.getMessage());
			e.printStackTrace();
		}

		// Find the server using UDP broadcast
		try {
			while (!mStopSignal) {
				// Get the username from the shared preferences
				SharedPreferences sp = PreferenceManager
						.getDefaultSharedPreferences(WalkieTalkieApplication.getContext());
				String username = sp.getString("prefUsername", "unknown");
				
				// Create the packet data from the token + the device's username
				String packetStr = BROADCAST_TOKEN + username;
				if (packetStr.length() > MAX_PACKET_SIZE) {
					packetStr = packetStr.substring(0, MAX_PACKET_SIZE-2); // STRING MUST END WITH '\0'!
				}
				byte[] sendData = packetStr.getBytes();
				Log.v(DEBUG_TAG, mOwnIP + "-> Data to send = " + packetStr);

				// Try the generic broadcast address first (255.255.255.255)
				sendPacket(mDsocket, GENERIC_BROADCAST_ADDRESS, UDPBroadcastReceiver.RECEIVER_PORT, sendData);

				// Broadcast the message over all the network interfaces
				Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
				while (interfaces.hasMoreElements()) {
					NetworkInterface networkInterface = interfaces.nextElement();

					if (networkInterface.isLoopback() || !networkInterface.isUp()) {
						continue; // Don't want to broadcast to the loopback interface
					}

					for (InterfaceAddress interfaceAddress : networkInterface.getInterfaceAddresses()) {
						InetAddress broadcast = interfaceAddress.getBroadcast();
						if (broadcast == null) {
							continue;
						}

						// Send the broadcast package!
						sendPacket(mDsocket, broadcast.getHostAddress(), UDPBroadcastReceiver.RECEIVER_PORT, sendData);
					}
				}
				Thread.sleep(BROADCAST_INTERVAL);
			}

		} catch (Exception e) {
			Log.e(DEBUG_TAG, "Exception while sending broadcast: " + e.getMessage());
			e.printStackTrace();
		} finally {
			// Close the socket in the end
			mDsocket.close();
			Log.d(DEBUG_TAG, "Closed datagram socket on " + mOwnIP);
		}
	}

	private void sendPacket(DatagramSocket dsocket, String address, int port, byte[] data) throws IOException {
			DatagramPacket sendPacket = new DatagramPacket(data, data.length, InetAddress.getByName(address), port);
			dsocket.send(sendPacket);
			Log.v(DEBUG_TAG, "<<< Announce packet sent from " + mOwnIP + " to: " + address.toString() + " : " + data.toString());
	}
}
